package main

import (
	"archive/zip"
	"bytes"
	"compress/flate"
	"encoding/base64"
	"fmt"
	"io"

	log "github.com/sirupsen/logrus"
)

// compress data
func deflate(content []byte) ([]byte, error) {
	buf := bytes.NewBuffer(nil)
	w, err := flate.NewWriter(buf, flate.DefaultCompression)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("flate writer fail")
		return nil, fmt.Errorf("deflate data fail")
	}
	defer w.Close()
	_, err = w.Write(content)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("compress data fail")
		return nil, fmt.Errorf("deflate data fail")
	}
	w.Close()
	return buf.Bytes(), nil
}

func CQEncode(content []byte) []byte {
	buf, _ := deflate(content)
	for idx, ele := range buf {
		buf[idx] = ele + 67
	}
	size := base64.StdEncoding.EncodedLen(len(buf))
	dst := make([]byte, size)
	base64.StdEncoding.Encode(dst, buf)
	return dst
}

func CQDecode(content []byte) []byte {
	size := base64.StdEncoding.DecodedLen(len(content))
	zipped := make([]byte, size)
	n, _ := base64.StdEncoding.Decode(zipped, content)
	zipped = zipped[:n]
	for idx, ele := range zipped {
		zipped[idx] = ele - 67
	}
	buf, _ := inflate(zipped)
	return buf
}

// decompress data
func inflate(content []byte) ([]byte, error) {
	r := flate.NewReader(bytes.NewReader(content))
	defer r.Close()
	result, err := io.ReadAll(r)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error()
		return nil, fmt.Errorf("inflate data fail")
	}
	return result, err
}

func zip_uncompress(content []byte) (string, []byte) {
	reader, err := zip.NewReader(bytes.NewReader(content), int64(len(content)))
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error()
		return "", []byte{}
	}
	for _, f := range reader.File {
		if f.Mode().IsDir() {
			continue
		}
		rc, err := f.Open()
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error()
			continue
		}
		body, err := io.ReadAll(rc)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error()
			continue
		}
		rc.Close()
		return f.Name, body
	}
	return "", []byte{}
}

func QXCDecode(content []byte) (string, []byte) {
	size := base64.StdEncoding.DecodedLen(len(content))
	zipped := make([]byte, size)
	n, _ := base64.StdEncoding.Decode(zipped, content)
	zipped = zipped[:n]
	for idx, ele := range zipped {
		zipped[idx] = ele - 67
	}
	return zip_uncompress(zipped)
}

func zip_compress(filename string, content []byte) []byte {
	w := bytes.NewBuffer(nil)
	writer := zip.NewWriter(w)
	f, err := writer.Create(filename)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error()
		return []byte{}
	}
	_, err = f.Write(content)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error()
		return []byte{}
	}
	writer.Flush()
	writer.Close()
	return w.Bytes()
}

func QXCEncode(filename string, content []byte) []byte {
	zipped := zip_compress(filename, content)
	for idx, ele := range zipped {
		zipped[idx] = ele + 67
	}
	size := base64.StdEncoding.EncodedLen(len(zipped))
	body := make([]byte, size)
	base64.StdEncoding.Encode(body, zipped)
	return body
}
